package org.javahispano.javacup.tacticas_aceptadas.jcrosas.chuky.util;

import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.Posicion;

/**Clase que representa coordenadas espaciales, con tipo primitivo double*/
public class PosicionCampo {
	private final double x,  y;

    /**Instancia una nueva posicion en 0,0*/
    public PosicionCampo() {
        x = 0;
        y = 0;
    }

    /**Instancia una nueva posicion copiando de otra posicion*/
    public PosicionCampo(PosicionCampo posicion) {
        x = posicion.getX();
        y = posicion.getY();
    }
    
    public PosicionCampo( Posicion posicion ) {
    	x = posicion.getX();
    	y = posicion.getY();
    }
    
    public boolean isDentroArea() {
    	double mx = Constantes.ANCHO_AREA_GRANDE / 2;
    	double my1 = Constantes.LARGO_CAMPO_JUEGO / 2;
    	double my2 = Constantes.LARGO_CAMPO_JUEGO / 2 - Constantes.LARGO_AREA_GRANDE;
    	double p = (Constantes.LARGO_ARCO+1) / 2;
    	// iteracion extra
    	if( y < -my1 && x >= -p && x <= p ) {
    		return true;
    	}
    	
    	return ( x >= -mx && x <= mx &&
    			 y >= -my1 && y <= -my2 );
    }

    public boolean isDentroAreaRival() {
    	double mx = Constantes.ANCHO_AREA_GRANDE / 2;
    	double my1 = Constantes.LARGO_CAMPO_JUEGO / 2;
    	double my2 = Constantes.LARGO_CAMPO_JUEGO / 2 - Constantes.LARGO_AREA_GRANDE;
    	
    	return ( x >= -mx && x <= mx &&
    			 y <= my1 && y >= my2 );
    }
    
    public boolean isDentroCampo() {
		double mx = Constantes.ANCHO_CAMPO_JUEGO / 2;
        double my = Constantes.LARGO_CAMPO_JUEGO / 2;
        double p = (Constantes.LARGO_ARCO+1) / 2;
    	// iteracion extra
    	if( y < -my && x >= -p && x <= p ) {
    		return true;
    	}
        
        return (x >= -mx && x <= mx &&
                y >= -my && y <= my);
	}

    /**Mueve en un angulo y radio*/
    public PosicionCampo moverAngulo(double angulo, double radio) {
        return new PosicionCampo(x + Math.cos(angulo) * radio, y + Math.sin(angulo) * radio);
    }

    /**Mueve en un angulo y un radio, limitando solo a avanzar no mas de un radio maximo*/
    public PosicionCampo moverAngulo(double angulo, double radio, double radioMax) {
        return new PosicionCampo(x + Math.cos(angulo) * Math.min(radio, radioMax), y + Math.sin(angulo) * Math.min(radio, radioMax));
    }

    /**Mueve un delta x y delta y*/
    public PosicionCampo moverPosicion(double dx, double dy) {
        return new PosicionCampo(x + dx, y + dy);
    }

    /**Mueve un delta x y delta y, limitando por un radio maximo*/
    public PosicionCampo moverPosicion(double dx, double dy, double radioMax) {
    	PosicionCampo dest = new PosicionCampo(x + dx, y + dy);
        double angulo = angulo(dest);
        double radio = distancia(dest);
        return new PosicionCampo(x + Math.cos(angulo) * Math.min(radio, radioMax), y + Math.sin(angulo) * Math.min(radio, radioMax));
    }

    /**Instancia una nueva posicion*/
    public PosicionCampo(double x, double y) {
        this.x = x;
        this.y = y;
    }

    /**Obtiene la coordenada x*/
    public double getX() {
        return x;
    }

    /**Obtiene la coordenada y*/
    public double getY() {
        return y;
    }

    /**Establece la posicion igual a otra posicion, pero invertida en el eje Y*/
    public PosicionCampo getPosicionInvertida() {
        return new PosicionCampo(-x, -y);
    }

    /**Establece la posicion*/
    public PosicionCampo setPosicion(double x, double y) {
        return new PosicionCampo(x, y);
    }

    /**Limita la posicion a estar dentro del campo de juego*/
    public PosicionCampo setDentroCampoJuego() {
        double mx = Constantes.ANCHO_CAMPO_JUEGO / 2;
        double my = Constantes.LARGO_CAMPO_JUEGO / 2;
        double x0 = x, y0 = y;
        if (x0 > mx) {
            x0 = mx;
        }

        if (x0 < -mx) {
            x0 = -mx;
        }

        if (y0 > my) {
            y0 = my;
        }

        if (y0 < -my) {
            y0 = -my;
        }
        return new PosicionCampo(x0, y0);
    }

    /**Obtiene el angulo entre dos posiciones*/
    public double angulo(PosicionCampo p) {
        double angle = 0;
        double dx = p.x - x;
        double dy = p.y - y;
        if (dx > 0) {
            angle = Math.atan(dy / dx);
        } else if (dx < 0) {
            angle = Math.atan(dy / dx) + Math.PI;
        } else if (dy < 0) {
            angle = -Math.PI / 2;
        } else {
            angle = Math.PI / 2;
        }

        angle = angle % (Math.PI * 2d);
        if (angle >= Math.PI) {
            angle = angle - 2d * Math.PI;
        }
        return angle;
    }

    @Override
    public String toString() {
        return "" + x + ":" + y;
    }

    /**Obtiene la distancia entre dos posiciones
     */
    public double distancia(PosicionCampo p) {
        return Math.sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
    }

    /**Obtiene el indice de la posicion mas cercana a esta posicion*/
    public int indiceMasCercano(PosicionCampo[] pos) {
        double max = Double.MAX_VALUE;
        double dist;
        int idx = -1;
        for (int i = 0; i < pos.length; i++) {
            dist = norma(pos[i]);
            if (dist < max) {
                max = dist;
                idx = i;
            }

        }
        return idx;
    }

    /**Obtiene el indice de la posicion mas cercana a esta posicion, excluyendo una lista*/
    public int indiceMasCercano(PosicionCampo[] pos, int... excluir) {
        double max = Double.MAX_VALUE;
        double dist;
        int idx = -1;
        boolean found;
        for (int i = 0; i < pos.length; i++) {
            dist = norma(pos[i]);
            found = false;
            for (int k : excluir) {
                if (k == i) {
                    found = true;
                    break;
                }
            }
            if (!found && dist < max) {
                max = dist;
                idx = i;
            }

        }
        return idx;
    }

    /**Obtiene un arreglo ordenado de los indices mas cercanos a los mas lejanos*/
    public int[] indicesMasCercanos(PosicionCampo[] pos) {
        int[] tmp = new int[pos.length];
        double[] dst = new double[pos.length];
        for (int i = 0; i < pos.length; i++) {
            tmp[i] = i;
            dst[i] = this.norma(pos[i]);
        }
        int ii;
        double dd;
        for (int i = 0; i < pos.length; i++) {
            for (int j = i + 1; j < pos.length; j++) {
                if (dst[i] > dst[j]) {
                    dd = dst[j];
                    dst[j] = dst[i];
                    dst[i] = dd;
                    ii = tmp[j];
                    tmp[j] = tmp[i];
                    tmp[i] = ii;
                }
            }
        }
        return tmp;
    }

    /**Obtiene un arreglo ordenado de los indices mas cercanos a los mas lejanos, excluyendo una lista*/
    public int[] indicesMasCercanos(PosicionCampo[] pos, int... excluir) {
        int[] tmp = new int[pos.length];
        double[] dst = new double[pos.length];
        boolean found;
        for (int i = 0; i < pos.length; i++) {
            tmp[i] = i;
            found = false;
            for (int k : excluir) {
                if (k == i) {
                    dst[i] = Integer.MAX_VALUE;
                    found = true;
                    break;
                }
            }
            if (!found) {
                dst[i] = this.norma(pos[i]);
            }

        }
        int ii;
        double dd;
        for (int i = 0; i < pos.length; i++) {
            for (int j = i + 1; j < pos.length; j++) {
                if (dst[i] > dst[j]) {
                    dd = dst[j];
                    dst[j] = dst[i];
                    dst[i] = dd;
                    ii = tmp[j];
                    tmp[j] = tmp[i];
                    tmp[i] = ii;
                }
            }
        }
        return tmp;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final PosicionCampo other = (PosicionCampo) obj;
        if (this.x != other.x) {
            return false;
        }
        if (this.y != other.y) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        return hash;
    }

    /**Retorna la interseccion entre 2 rectas*/
    public static PosicionCampo Interseccion(PosicionCampo n1, PosicionCampo n2, PosicionCampo m1, PosicionCampo m2) {
        double a1 = n1.y - n2.y;
        double b1 = n2.x - n1.x;
        double c1 = n1.x * n2.y - n2.x * n1.y;

        double a2 = m1.y - m2.y;
        double b2 = m2.x - m1.x;
        double c2 = m1.x * m2.y - m2.x * m1.y;

        double d = a1 * b2 - a2 * b1;
        double d1 = -c1 * b2 + c2 * b1;
        double d2 = -a1 * c2 + a2 * c1;

        if (d != 0) {
            return new PosicionCampo(d1 / d, d2 / d);
        } else {
            return null;
        }
    }

    /**Retorna el punto medio*/
    public static PosicionCampo media(PosicionCampo n1, PosicionCampo n2) {
        return new PosicionCampo((n1.x + n2.x) / 2, (n1.y + n2.y) / 2);
    }

    /**Define una norma para comparar distancias, es mas rapido el calculo que usar el metodos distancia*/
    public double norma() {
        return x * x + y * y;
    }

    /**Define una norma para comparar distancias, es mas rapido el calculo que usar el metodos distancia*/
    public double norma(PosicionCampo p) {
        double dx = p.x - x;
        double dy = p.y - y;
        return dx * dx + dy * dy;
    }
    
    
    
    public Posicion toPosicion() {
    	return new Posicion( x, y );
    }
}

